//////////////////////////////////////////////////////////////////////////////////
//	 Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#if defined(_WIN32)
#include "def.h"
#include "wx/msw/registry.h"
#include "wx/msw/private.h"
#include "sysdepapi.h"
#include <Iphlpapi.h>

#pragma comment(lib, "Iphlpapi")

#define AUTO_RUN_NAME wxT("routercmd")

static bool gbInsidecall=false;

struct CwxExecGuard
{
	CwxExecGuard()
	{
		if ( gbInsidecall ) THROW_EXC(cERWrongObjectState);

		gbInsidecall = true;
	}

	~CwxExecGuard()
	{
		gbInsidecall = false;
	}
};

#define EXEC_FLAGS wxEXEC_NODISABLE|wxEXEC_SYNC

CSysDepAPI::CSysDepAPI(void)
{
}

CSysDepAPI::~CSysDepAPI(void)
{
}

bool CSysDepAPI::IsLoadedAtStartUp()
{
	bool bRes(false);
	wxString strKey;

	wxLog::EnableLogging(false);

	wxRegKey regAutoRun(wxRegKey::HKLM, wxT("Software\\Microsoft\\Windows\\CurrentVersion\\Run") );

	if (regAutoRun.Open(wxRegKey::Read))
	{
		if (regAutoRun.QueryValue(AUTO_RUN_NAME, strKey))
		{
			bRes = true;			
		}
	}

	wxLog::EnableLogging();

	return bRes;
}

void CSysDepAPI::SetLoadAtStartUp(bool bVal)
{
	wxRegKey regAutoRun(wxRegKey::HKLM, wxT("Software\\Microsoft\\Windows\\CurrentVersion\\Run") );
	wxString strKey;

	TCHAR szName[MAX_PATH];
	GetModuleFileName(NULL, szName, MAX_PATH );

	strKey=szName;
	strKey+=wxT(" --");
	strKey+=SWITCH_SHOW_MINIMIZED;

	if (regAutoRun.Open(wxRegKey::Write))
	{
		if (bVal)
		{
			regAutoRun.SetValue(AUTO_RUN_NAME, strKey);
		}
		else
		{	
			regAutoRun.DeleteValue( AUTO_RUN_NAME );
		}	
	}
}

std::string CSysDepAPI::GetDefGateWay()
{
	std::string strRes;

	PIP_ADAPTER_INFO pAdapterInfo;
	PIP_ADAPTER_INFO pAdapter = NULL;

	pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
	ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);

	if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
	{
		free(pAdapterInfo);
		pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
	}

	if ( pAdapterInfo )
	{
		if ( GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)== NO_ERROR )
		{
			pAdapter = pAdapterInfo;

			while ( pAdapter )
			{
				if ( pAdapter->Type == MIB_IF_TYPE_ETHERNET )
				{
					strRes = (const char*)pAdapter->GatewayList.IpAddress.String;

					if ( !strRes.empty() )
						break;
				}

				pAdapter = pAdapter->Next;
			}
		}

		free(pAdapterInfo);
	}

	return strRes;
}

IPLIST CSysDepAPI::GetAddressList()
{
	IPLIST list;

	PIP_ADAPTER_INFO pAdapterInfo;
	PIP_ADAPTER_INFO pAdapter = NULL;

	pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
	ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);

	if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
	{
		free(pAdapterInfo);
		pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
	}

	if ( pAdapterInfo )
	{
		if ( GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)== NO_ERROR )
		{
			pAdapter = pAdapterInfo;

			while ( pAdapter )
			{
				if ( pAdapter->Type == MIB_IF_TYPE_ETHERNET )
				{
					std::string strAddr = pAdapter->IpAddressList.IpAddress.String;
					
					if ( strAddr!="0.0.0.0" )
						list.push_back( strAddr );
				}

				pAdapter = pAdapter->Next;
			}
		}

		free(pAdapterInfo);
	}


	return list;
}

wxString CSysDepAPI::GetRelevantLocalIPAddress(const wxString& strRouterAddr)
{
	wxString str, strAddr;

	IPLIST lst = GetAddressList();
	std::string strRouterIP = GetDefGateWay();

	for(size_t i=0;i<lst.size();i++)
	{
		strAddr = wxString::FromAscii( lst[i].c_str() );

		if ( strAddr.Left(3)==strRouterAddr.Left(3) )
		{
			str = strAddr;
			break;
		}
	}


	return str;
}

wxString CSysDepAPI::GetLocalIPSettings()
{
	wxString strRes;

	wxArrayString output;
	CwxExecGuard guard;
	
	wxExecute( wxT("ipconfig /all"), output, EXEC_FLAGS );  
	if ( !output.IsEmpty() )
	{
		for (size_t i=0;i<  output.GetCount();i++)
		{
			if (!strRes.IsEmpty())
				strRes+=wxT("\n");
			strRes+= output.Item(i); 
		}
	}

	return strRes;
}

wxString CSysDepAPI::GetLocalRoutingTable()
{
	wxString strRes;

	wxArrayString output;
	CwxExecGuard guard;
	wxExecute( wxT("route PRINT"), output, EXEC_FLAGS );  

	if ( !output.IsEmpty() )
	{
		for (size_t i=0;i<  output.GetCount();i++)
		{
			if (!strRes.IsEmpty())
				strRes+=wxT("\n");
			strRes+= output.Item(i); 
		}
	}

	return strRes;
}

//GetPerTcpConnectionEStats 
//GetOwnerModuleFromTcpEntry
//GetExtendedUdpTable

//Is detect server app is possible for UDP?
PROTOLIST CSysDepAPI::GetUDPProtoList()
{
	PROTOLIST lst;

	/*
	DWORD dwSize(0);

	void* pMem = malloc(1);

	DWORD dwRes = GetExtendedUdpTable(pMem, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE,0);

	if ( dwRes == ERROR_INSUFFICIENT_BUFFER )
	{
		pMem = realloc(pMem, dwSize );
		dwRes = GetExtendedUdpTable(pMem, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_MODULE,0);
	}

	if ( NO_ERROR==dwRes )
	{
		PMIB_UDPTABLE_OWNER_MODULE pTable = (PMIB_UDPTABLE_OWNER_MODULE)pMem;

		for(DWORD i=0;i<pTable->dwNumEntries;i++)
		{
			MIB_UDPROW_OWNER_MODULE& mod = pTable->table[i];

			

		}
	}*/

	return lst;
}

PROTOLIST CSysDepAPI::GetTCPProtoList(const wxString& strRouterIP)
{
	PROTOLIST lst;
	DWORD dwSize(1024);
	void* pMem = malloc(dwSize);

	std::string strIP = (const char*)GetRelevantLocalIPAddress( strRouterIP ).ToAscii();

	DWORD dwRes = GetExtendedTcpTable(pMem, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_LISTENER,0);

	if ( dwRes == ERROR_INSUFFICIENT_BUFFER )
	{
		pMem = realloc(pMem, dwSize );
		dwRes = GetExtendedTcpTable(pMem, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_MODULE_LISTENER,0);
	}

	if ( NO_ERROR==dwRes )
	{
		PMIB_TCPTABLE_OWNER_MODULE pTable = (PMIB_TCPTABLE_OWNER_MODULE)pMem;

		LPVOID nameBuff = malloc(1024); 
		DWORD  dwInfoSize;

		for(DWORD i=0;i<pTable->dwNumEntries;i++)
		{
			PTCPIP_OWNER_MODULE_BASIC_INFO pInfo = (PTCPIP_OWNER_MODULE_BASIC_INFO)nameBuff;

			MIB_TCPROW_OWNER_MODULE& mod = pTable->table[i];

			in_addr addr;
			addr.s_addr = mod.dwLocalAddr;
			std::string strAddr = inet_ntoa(addr);

			if ( !mod.dwLocalAddr || strAddr == strIP )
			{
				dwInfoSize = 1024;

				//May require this: manifest file with a requestedExecutionLevel set to requireAdministrator
				dwRes = GetOwnerModuleFromTcpEntry(&mod, TCPIP_OWNER_MODULE_INFO_BASIC, nameBuff, &dwInfoSize);

				if ( NO_ERROR==dwRes )
				{
					PROTOENTRY entry;
					entry.strOwnerName = pInfo->pModuleName;
					entry.strProcName = pInfo->pModuleName;

					if ( !entry.strOwnerName.IsEmpty() )
					{
						entry.proto = cPortFrorwardProtoTCPIP;
						entry.wPort = (unsigned short)ntohs(mod.dwLocalPort);
						entry.strIP = wxString::FromAscii( strIP.c_str() );
						entry.strOwnerName+=wxString::Format(wxT(":%u"), (DWORD)entry.wPort);
						lst.push_back(entry);
					}
				}
			}
		}

		free(nameBuff);
	}

	free(pMem);

	return lst;
}

MACLIST CSysDepAPI::GetPCInterfacesMACList()
{
	MACLIST list;

	PIP_ADAPTER_INFO pAdapterInfo;
	PIP_ADAPTER_INFO pAdapter = NULL;

	pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
	ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);

	if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
	{
		free(pAdapterInfo);
		pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
	}

	if ( pAdapterInfo )
	{
		if ( GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)== NO_ERROR )
		{
			pAdapter = pAdapterInfo;

			while ( pAdapter )
			{
				if ( pAdapter->Type == MIB_IF_TYPE_ETHERNET )
				{
					std::string strAddr = pAdapter->IpAddressList.IpAddress.String;
					
					if ( strAddr!="0.0.0.0" )
					{
						std::string strAddr;
						char buf[8];

						for (size_t i=0;i<pAdapter->AddressLength;i++)
						{
							sprintf(buf, "%2.2X", pAdapter->Address[i]);

							if ( !strAddr.empty() )
								strAddr+=':';

							strAddr+=buf;
						}

						if (!strAddr.empty())
						{
							MACENTRY entry;
							entry.strMAC = strAddr;

							list.push_back(entry);
						}
					}
				}

				pAdapter = pAdapter->Next;
			}
		}

		free(pAdapterInfo);
	}

	return list;
}

#endif
